//
//  YYStateMachineDynamic.m
//  LearnObjectiveC
//
//  Created by client on 2017/9/18.
//  Copyright © 2017年 吴可高. All rights reserved.
//

#import "YYStateMachineDynamic.h"
#import "YYEvent.h"
#import "YYStateMachine.h"
#import "YYTransition.h"
#import "YYStateMachineTypedefiner.h"
#include <stdio.h>
#include <stdbool.h>

extern void * YYStateMachineDefinitionKey;
bool YYStateMachineTriggerEvent(id self,SEL _cmd);
bool YYStateMachineTriggerEvent(id self,SEL _cmd)
{
    NSString * currentState = [self performSelector:@selector(state)];
    YYStateMachine *sm = [[self class] performSelector:@selector(stateMachine)];
    NSString * eventName = NSStringFromSelector(_cmd);
    NSString * nextState = [sm nextStateFrom:currentState forEvent:eventName];
    if (nextState) {
        YYEvent * event = [sm eventWithName:eventName];
        NSArray * beforeCallbacks = event.beforeCallbacks;
        for (void(^beforeCallback)(id) in beforeCallbacks) {
            beforeCallback(self);
        }
        [self performSelector:@selector(setState:) withObject:nextState];
        NSArray * afterCallbacks = event.afterCallbacks;
        for (void(^afterCallback)(id) in afterCallbacks) {
             afterCallback(self);
        }
        return YES;
    }
    else
    {
        return NO;
    }
}

bool YYStateMachineCheckCanTransition(id self,SEL _cmd)
{
    YYStateMachine * sm =[[self class]performSelector:_cmd];
    NSString *currentState = [self performSelector:@selector(state)];
    NSString *query = [[NSStringFromSelector(_cmd) substringFromIndex:3] lowercaseString];
    NSString * nextState = [sm nextStateFrom:currentState forEvent:query];
    return nextState != nil;
}

bool YYStateMachineCheckState(id self, SEL _cmd)
{
    NSString * currentState = [self performSelector:@selector(state)];
    NSString *query = [[NSStringFromSelector(_cmd) substringFromIndex:2] lowercaseString];
    return [query isEqualToString:currentState];
}

void YYStateMachineInitializeInstance(id self,SEL _cmd)
{
    YYStateMachine * sm = [[self class]performSelector:@selector(stateMachine)];
    [self performSelector:@selector(setState:) withObject:[sm initialState]];
}

void YYStateMachineInitializeClass(Class lass)
{
    YYStateMachine * sm = [lass performSelector:@selector(stateMachine)];
    for (YYEvent * event in sm.events) {
        class_addMethod(lass, NSSelectorFromString(event.name), (IMP) YYStateMachineTriggerEvent, "i@:");
        
        NSString * transitionQueryMethodName  = [NSString stringWithFormat:@"can%@",[event.name capitalizedString]];
        class_addMethod(lass, NSSelectorFromString(transitionQueryMethodName), (IMP) YYStateMachineCheckCanTransition, "i@:");
    }
    
    for (NSString * state in sm.states) {
        NSString *queryMethodName = [NSString stringWithFormat:@"is%@", [state capitalizedString]];
        class_addMethod(lass, NSSelectorFromString(queryMethodName), (IMP) YYStateMachineCheckState, "i@:");
    }
    class_addMethod(lass, @selector(initializeStateMachine), (IMP)(YYStateMachineInitializeInstance), "v@:");
}

YYStateMachine * YYStateMachineSetDefinitionForClass(Class lass,void(^definition)(YYStateMachine* sm))
{
    YYStateMachine * sm = (YYStateMachine*)objc_getAssociatedObject(lass, &YYStateMachineDefinitionKey);
    if (sm == nil) {
        sm = [[YYStateMachine alloc]init];
        objc_setAssociatedObject(lass, &YYStateMachineDefinitionKey, sm, OBJC_ASSOCIATION_RETAIN);
        definition(sm);
    }
    return sm;
}
